package gnss_plugs

import (
	"dgo/goutils/gnss"
	"dgo/goutils/utils"
	"fmt"
	"math"
	"os"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

/*
*

	推荐使用.rt
*/
type GnssRinexObs3xWriter struct {
	Id             string
	MountPoint     string
	Sys            byte
	bodyCnt        int
	curTmpFileName string
	useTempFile    bool // 是否使用临时文件,
	lastFileName   string
	Filefmt        string
	tmpPath        string
	SignIsOK       bool
	AutowireSign   bool // 自动更新sign
	signHeadstr    string
	signupflag     bool
	rinexHeader    *gnss.RinexHeader
	signstartT     time.Time
	startTime      time.Time
	currTime       time.Time // 当前时间的观测值
	FilterSys      byte
	FixInterval    int64 // 固定采样间隔 单位：秒  5s,  mod(t, 5) = 0

	closeW sync.WaitGroup

	// 存放一个历元的观测值
	obsList *gnss.EpochRinexObs

	obsCache []*gnss.RtcmMsm

	// 检测系统重入bug
	lastgid          uint64
	checkworkingflag int32
	lastdebuginfo    string
	DebuginfoFunc    func() string

	lastwrite_file_T   time.Time
	lastwrite_flag     int32
	lastwrite_currTime time.Time // 最后写入的观测值时间
	lastwrite_filename string
	lastwrite_T        time.Time
	lastwrite_goid     uint64
}

func NewGnssRinexObs3xWriter() *GnssRinexObs3xWriter {
	rval := &GnssRinexObs3xWriter{
		Id:          utils.RandKeyString(4),
		Filefmt:     "",
		currTime:    time.Time{},
		obsList:     gnss.NewEpochRinexObs(),
		rinexHeader: gnss.NewRinexHeader(),
		useTempFile: true,
	}
	return rval
}

func (this *GnssRinexObs3xWriter) ConfigUseTempFile(val bool) {
	this.useTempFile = val
}

func (this *GnssRinexObs3xWriter) checkNewFile(curT time.Time) bool {
	str := this.Filefmt
	str = utils.ParseTimeFmt(str, curT)
	return strings.Compare(this.lastFileName, str) != 0
}

func (this *GnssRinexObs3xWriter) checkFileStart() {
	if !this.useTempFile {
		if this.startTime.IsZero() {
			this.startTime = time.Now()
		}
		strFileN := utils.ParseTimeFmt(this.Filefmt, this.startTime)
		this.lastFileName = strFileN
		utils.ForceCreateFilePath(this.lastFileName)
		this.writeRinexHeader2File(this.lastFileName, this.signHeadstr, utils.ZeroTime, utils.ZeroTime)
		return
	}

	if len(this.curTmpFileName) == 0 {
		if this.startTime.IsZero() {
			this.startTime = time.Now()
		}
		strFileN := utils.ParseTimeFmt(this.Filefmt, this.startTime)
		this.lastFileName = strFileN
		//this.curTmpFileName = TempFilesDirectory + utils.PathSeparator + fmt.Sprintf("~obs.%s.%s.tmp", utils.RandKeyString(5), this.MountPoint)
		this.curTmpFileName = strFileN + fmt.Sprintf(".obs.%s.tmp", utils.RandKeyString(5))
		utils.ForceCreateFilePath(this.curTmpFileName)
		utils.ForceCreateFilePath(this.lastFileName)
	}
}

func (this *GnssRinexObs3xWriter) checkFileEnd() {
	if !this.useTempFile {
		this.startTime = utils.ZeroTime
		this.bodyCnt = 0
		return
	}

	if len(this.curTmpFileName) == 0 {
		return
	}

	tmp_cur_file := this.curTmpFileName
	temp_starttime := this.startTime
	temp_signHeader := this.signHeadstr
	tmp_filefmt := this.Filefmt
	tmp_currTime := this.currTime

	// 异步存储文件避免堵塞
	utils.GoFunc2(func() {
		if !atomic.CompareAndSwapInt32(&this.lastwrite_flag, 0, 1) {
			strName := utils.ParseTimeFmt(tmp_filefmt, temp_starttime)
			fmt.Fprintf(os.Stderr, "[警告][%s]上次存储尚未完成 \r\n  ->上次 goid:%d, 时间:%s, 观测值时间:%s, 文件名: %s\r\n  ->本次 观测值时间:%s, 文件名: %s\r\n",
				utils.NowString(),
				this.lastwrite_goid, utils.DateTimeString2(this.lastwrite_T), utils.DateTimeString2(this.lastwrite_currTime), this.lastwrite_filename,
				utils.DateTimeString2(tmp_currTime), strName)
			return
		}
		this.closeW.Add(1)
		defer func() {
			this.lastwrite_flag = 0
			this.closeW.Done()
		}()

		strName := utils.ParseTimeFmt(tmp_filefmt, temp_starttime)
		this.lastwrite_filename = strName
		this.lastwrite_T = time.Now()
		this.lastwrite_goid = utils.GetCurrentGoRoutineID()
		this.lastwrite_currTime = tmp_currTime

		// first line
		//str := gnss.PrintRinexFV(3.04, 2, 9) + FPrintRinexStrV("OBSERVATION DATA", 27) + FPrintRinexStrV("M (MIXED)", 13)
		//str = utils.AddSuffixForWidth(str, 60, " ")
		//str += "RINEX VERSION / TYPE\n"
		//
		//if temp_starttime.IsZero() {
		//	temp_starttime = time.Now()
		//}
		//
		//str += utils.AddSuffixForWidth(RINEX_TransVer, 20, " ") +
		//	utils.AddSuffixForWidth(RINEX_CopyRight, 20, " ") +
		//	utils.AddSuffixForWidth(temp_starttime.Format("20060102 150405")+" UTC", 20, " ") +
		//	"PGM / RUN BY / DATE\n"
		//
		//str += temp_signHeader
		//str += "  " + temp_starttime.Format("2006    01    02    15    04   05") + ".0000000     GPS         TIME OF FIRST OBS\n"
		//str += "  " + tmp_currTime.Format("2006    01    02    15    04   05") + ".0000000     GPS         TIME OF LAST OBS\n"
		//str += utils.AddSuffixForWidth("", 60, " ") + "END OF HEADER\n"

		//utils.NewFileData(strName, []byte(str))

		this.writeRinexHeader2File(strName, temp_signHeader, temp_starttime, tmp_currTime)

		utils.AppendAllFile(strName, tmp_cur_file)
		os.Remove(tmp_cur_file)

	})
	this.curTmpFileName = ""
	this.startTime = utils.ZeroTime
	this.bodyCnt = 0
}

func (this *GnssRinexObs3xWriter) writeRinexHeader2File(filename string, temp_signHeader string, temp_starttime, end_time time.Time) {
	// first line
	str := gnss.PrintRinexFV(3.04, 2, 9) + FPrintRinexStrV("OBSERVATION DATA", 27) + FPrintRinexStrV("M (MIXED)", 13)
	str = utils.AddSuffixForWidth(str, 60, " ")
	str += "RINEX VERSION / TYPE\n"

	str += utils.AddSuffixForWidth(RINEX_TransVer, 20, " ") +
		utils.AddSuffixForWidth(RINEX_CopyRight, 20, " ") +
		utils.AddSuffixForWidth(temp_starttime.Format("20060102 150405")+" UTC", 20, " ") +
		"PGM / RUN BY / DATE\n"

	str += temp_signHeader
	if !temp_starttime.IsZero() {
		str += "  " + temp_starttime.Format("2006    01    02    15    04   05") + ".0000000     GPS         TIME OF FIRST OBS\n"
		str += "  " + end_time.Format("2006    01    02    15    04   05") + ".0000000     GPS         TIME OF LAST OBS\n"
	}
	str += utils.AddSuffixForWidth("", 60, " ") + "END OF HEADER\n"

	utils.NewFileData(filename, []byte(str))
}

func (this *GnssRinexObs3xWriter) Stop() {

	this.CheckPostCache()
	this.writePreRinexObsEpoch()
	this.checkFileEnd()
	time.Sleep(time.Millisecond * 100)
	this.closeW.Wait()
}

func (this *GnssRinexObs3xWriter) OnRecvGnssRecCallBack(sender interface{}, channelid string, subscribeid interface{}, data interface{}, tag interface{}, isBreak *bool) bool {
	if atomic.CompareAndSwapInt32(&this.checkworkingflag, 0, 1) {
		this.lastgid = utils.GetCurrentGoRoutineID()
		if this.DebuginfoFunc != nil {
			this.lastdebuginfo = this.DebuginfoFunc()
		}
		defer func() {
			this.checkworkingflag = 0
		}()
		if data == nil {
			this.checkFileEnd()
			return true
		}
		rtcmRec, ok := data.(*gnss.GnssRtcmData)
		if ok {
			this.OnRecvGnssDataRec(sender, rtcmRec)
		}
		return true
	} else {
		lastid := this.lastgid
		curr_gid := utils.GetCurrentGoRoutineID()
		debuginfo := "[需要设定debuginfofunc,进行获取信息]"
		if this.DebuginfoFunc != nil {
			debuginfo = this.DebuginfoFunc()
		}
		str := fmt.Sprintf("[并发重入]%s(%s), current_gid:%d\r\n debuginfo:%s\r\n "+"==============\r\n"+
			"lastgid:%d\r\n lastdebuginfo:%s\r\n", this.Id, this.MountPoint,
			curr_gid, debuginfo,
			lastid, this.lastdebuginfo)
		utils.Log.WarnTypef("GNSS-BUG", str)
		return false
	}

}

/*
*
 */
func (this *GnssRinexObs3xWriter) checkSignHeadStr() bool {
	if len(this.signHeadstr) == 0 {
		this.signHeadstr = this.rinexHeader.PrintSignHeader()
		return true
	}

	return false
}

/**
 * 添加成功返回true, 已经存在返回false
 */
func (this *GnssRinexObs3xWriter) CheckAddSign(sys byte, signv byte) bool {
	return this.rinexHeader.CheckAddSign(sys, signv)
}

func (this *GnssRinexObs3xWriter) checkSignUpdate(rtcmTime time.Time, msm *gnss.RtcmMsm) bool {
	this.signupflag = false
	if this.SignIsOK {
		return true
	}

	if msm.Header.SigN == 0 {
		return false
	}

	if this.signstartT.IsZero() {
		this.signstartT = rtcmTime
	}

	if this.AutowireSign || utils.SecondOf(this.signstartT, rtcmTime) < 30 {
		h := msm.Header
		up := false
		var k byte
		for k = 0; k < h.SigN; k++ {
			sigi := h.Sigs[k]
			if this.CheckAddSign(msm.Sys, sigi) {
				up = true
			}
		}

		if up {
			this.signupflag = true
			this.signHeadstr = ""
		}
	}

	return utils.SecondOf(this.signstartT, rtcmTime) >= 30
}

/**
 * 写入前一秒的观测值到文件
 */
func (this *GnssRinexObs3xWriter) writePreRinexObsEpoch() {
	if this.bodyCnt == 0 {
		return
	}

	defer func() {
		this.bodyCnt = 0
	}()

	if this.FixInterval > 0 {
		f_msec := float64(this.currTime.UnixNano()) / 1e6 // 毫秒 采样率有 10Hz的数据
		i_msec := int64(math.Floor(f_msec + 0.1))
		v := i_msec % (this.FixInterval * 1000)
		if v != 0 {
			//utils.Log.Debugf("固定采样率为:%d,最后时间:%s", this.FixInterval, utils.DateTimeString(this.currTime))
			return
		}
	}

	n, strRinex := this.obsList.ToRinex3String(this.rinexHeader)
	if n == 0 {
		return
	}

	//this.currTime.Format()
	str := "> " + this.currTime.Format("2006 01 02 15 04 05.0000000") + "  0 " + fmt.Sprintf("%.2d", n)
	str += "\n"
	str += strRinex

	if this.checkNewFile(this.currTime) {
		this.checkFileEnd() // 上个文件处理

		this.startTime = this.currTime

		if this.startTime.IsZero() {
			this.startTime = time.Now()
		}

		// 初始化新文件
		this.checkFileStart()
	}

	if this.useTempFile {
		this.lastwrite_file_T = time.Now()
		_, err := utils.AppendFile(this.curTmpFileName, []byte(str))
		if err != nil {
			utils.Log.Errf("写入出现异常:%s, 文件:%s", err.Error(), this.curTmpFileName)
		}
	} else {
		this.lastwrite_file_T = time.Now()
		_, err := utils.AppendFile(this.lastFileName, []byte(str))
		if err != nil {
			utils.Log.Errf("写入出现异常:%s, 文件:%s", err.Error(), this.lastFileName)
		}
	}

}

func (this *GnssRinexObs3xWriter) processObsMsm(obs *gnss.RtcmMsm) {
	if this.checkSignHeadStr() {
		// header 发生了改变

	}

	warnmsg := obs.ProcObs()
	if len(warnmsg) > 0 {
		utils.Log.WarnTypef("RINEX", "[%s][%s]:%s", this.MountPoint, this.currTime.Format("2006 01 02 15 04 05"), warnmsg)
	}
	if gnss.CONFIG_OUT_DEBUG_RINEX_HEADER {
		utils.Log.DebugTypef("TRACE_OBS", "[%s] \r\n%s \r\nRTCM:%s", this.currTime.Format("2006 01 02 15 04 05"), obs.String(), utils.BufToHexStr(obs.RawBuf, 0, " "))
	}

	if obs.MsmTime != this.currTime {
		this.writePreRinexObsEpoch()
		this.obsList.Save2Pre()
		this.obsList.Reset()
		this.currTime = obs.MsmTime
		this.obsList.AddMsm(obs)
		this.bodyCnt++

	} else if obs.Header.Sync == 0 {
		this.obsList.AddMsm(obs)
		this.bodyCnt++
		this.writePreRinexObsEpoch()
		this.obsList.Save2Pre()
		this.obsList.Reset()
	} else {
		this.obsList.AddMsm(obs)
		this.bodyCnt++
	}
}

func (this *GnssRinexObs3xWriter) WriteDelaySecs() float64 {
	return time.Now().Sub(this.lastwrite_file_T).Seconds()
}

func (this *GnssRinexObs3xWriter) CheckPostCache() {
	if len(this.obsCache) > 0 {
		for _, rec := range this.obsCache {
			this.processObsMsm(rec)
		}
		this.obsCache = make([]*gnss.RtcmMsm, 0)
	}
}

func (this *GnssRinexObs3xWriter) OnRecvGnssDataRec(sender interface{}, rec *gnss.GnssRtcmData) {
	//if rec.RtcmTime.IsZero(){
	//	rec.RtcmTime = utils.ZeroTime;
	//}
	if rec.TypeId == gnss.TYPE_OBS {
		if obs, ok := rec.Data.(*gnss.RtcmMsm); ok {
			obs.MsmTime = rec.RtcmTime
			// 检测是否需要更新信号
			if !this.checkSignUpdate(rec.RtcmTime, obs) {
				if this.FilterSys == 0 || rec.Sys == this.FilterSys {
					if this.obsCache == nil {
						this.obsCache = make([]*gnss.RtcmMsm, 0)
					}
					this.obsCache = append(this.obsCache, obs)
				}
				return
			}

			if this.FilterSys != 0 && rec.Sys != this.FilterSys {
				return
			}

			this.CheckPostCache()

			this.processObsMsm(obs)

		}
	}

}
